﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;
using NetTopologySuite.GeometriesGraph;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Operation.Overlay
{
	public class PolygonBuilder
	{
		public PolygonBuilder(IGeometryFactory geometryFactory)
		{
			this._geometryFactory = geometryFactory;
		}

		public void Add(PlanarGraph graph)
		{
			this.Add(graph.EdgeEnds, graph.Nodes);
		}

		public void Add(IList<EdgeEnd> dirEdges, IList<Node> nodes)
		{
			PlanarGraph.LinkResultDirectedEdges(nodes);
			List<EdgeRing> maxEdgeRings = this.BuildMaximalEdgeRings(dirEdges);
			List<EdgeRing> freeHoleList = new List<EdgeRing>();
			PolygonBuilder.SortShellsAndHoles(this.BuildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList), this._shellList, freeHoleList);
			PolygonBuilder.PlaceFreeHoles(this._shellList, freeHoleList);
		}

		public IList<IGeometry> Polygons
		{
			get
			{
				return this.ComputePolygons(this._shellList);
			}
		}

		private List<EdgeRing> BuildMaximalEdgeRings(IEnumerable<EdgeEnd> dirEdges)
		{
			List<EdgeRing> list = new List<EdgeRing>();
			foreach (EdgeEnd edgeEnd in dirEdges)
			{
				DirectedEdge directedEdge = (DirectedEdge)edgeEnd;
				if (directedEdge.IsInResult && directedEdge.Label.IsArea() && directedEdge.EdgeRing == null)
				{
					MaximalEdgeRing maximalEdgeRing = new MaximalEdgeRing(directedEdge, this._geometryFactory);
					list.Add(maximalEdgeRing);
					maximalEdgeRing.SetInResult();
				}
			}
			return list;
		}

		private List<EdgeRing> BuildMinimalEdgeRings(List<EdgeRing> maxEdgeRings, IList<EdgeRing> shellList, IList<EdgeRing> freeHoleList)
		{
			List<EdgeRing> list = new List<EdgeRing>();
			foreach (EdgeRing edgeRing in maxEdgeRings)
			{
				MaximalEdgeRing maximalEdgeRing = (MaximalEdgeRing)edgeRing;
				if (maximalEdgeRing.MaxNodeDegree > 2)
				{
					maximalEdgeRing.LinkDirectedEdgesForMinimalEdgeRings();
					IList<EdgeRing> list2 = maximalEdgeRing.BuildMinimalRings();
					EdgeRing edgeRing2 = PolygonBuilder.FindShell(list2);
					if (edgeRing2 != null)
					{
						PolygonBuilder.PlacePolygonHoles(edgeRing2, list2);
						shellList.Add(edgeRing2);
						continue;
					}
					IEnumerator<EdgeRing> enumerator2 = list2.GetEnumerator();
					{
						while (enumerator2.MoveNext())
						{
							EdgeRing item = enumerator2.Current;
							freeHoleList.Add(item);
						}
						continue;
					}
				}
				list.Add(maximalEdgeRing);
			}
			return list;
		}

		private static EdgeRing FindShell(IEnumerable<EdgeRing> minEdgeRings)
		{
			int num = 0;
			EdgeRing result = null;
			foreach (EdgeRing edgeRing in minEdgeRings)
			{
				if (!edgeRing.IsHole)
				{
					result = edgeRing;
					num++;
				}
			}
			Assert.IsTrue(num <= 1, "found two shells in MinimalEdgeRing list");
			return result;
		}

		private static void PlacePolygonHoles(EdgeRing shell, IEnumerable<EdgeRing> minEdgeRings)
		{
			foreach (EdgeRing edgeRing in minEdgeRings)
			{
				MinimalEdgeRing minimalEdgeRing = (MinimalEdgeRing)edgeRing;
				if (minimalEdgeRing.IsHole)
				{
					minimalEdgeRing.Shell = shell;
				}
			}
		}

		private static void SortShellsAndHoles(IEnumerable<EdgeRing> edgeRings, IList<EdgeRing> shellList, IList<EdgeRing> freeHoleList)
		{
			foreach (EdgeRing edgeRing in edgeRings)
			{
				edgeRing.SetInResult();
				if (edgeRing.IsHole)
				{
					freeHoleList.Add(edgeRing);
				}
				else
				{
					shellList.Add(edgeRing);
				}
			}
		}

		private static void PlaceFreeHoles(IList<EdgeRing> shellList, IEnumerable<EdgeRing> freeHoleList)
		{
			foreach (EdgeRing edgeRing in freeHoleList)
			{
				if (edgeRing.Shell == null)
				{
					EdgeRing edgeRing2 = PolygonBuilder.FindEdgeRingContaining(edgeRing, shellList);
					if (edgeRing2 == null)
					{
						throw new TopologyException("unable to assign hole to a shell", edgeRing.GetCoordinate(0));
					}
					edgeRing.Shell = edgeRing2;
				}
			}
		}

		private static EdgeRing FindEdgeRingContaining(EdgeRing testEr, IEnumerable<EdgeRing> shellList)
		{
			ILinearRing linearRing = testEr.LinearRing;
			Envelope envelopeInternal = linearRing.EnvelopeInternal;
			Coordinate coordinateN = linearRing.GetCoordinateN(0);
			EdgeRing edgeRing = null;
			Envelope envelope = null;
			foreach (EdgeRing edgeRing2 in shellList)
			{
				ILinearRing linearRing2 = edgeRing2.LinearRing;
				Envelope envelopeInternal2 = linearRing2.EnvelopeInternal;
				if (edgeRing != null)
				{
					envelope = edgeRing.LinearRing.EnvelopeInternal;
				}
				bool flag = false;
				if (envelopeInternal2.Contains(envelopeInternal) && CGAlgorithms.IsPointInRing(coordinateN, linearRing2.Coordinates))
				{
					flag = true;
				}
				if (flag && (edgeRing == null || envelope.Contains(envelopeInternal2)))
				{
					edgeRing = edgeRing2;
				}
			}
			return edgeRing;
		}

		private IList<IGeometry> ComputePolygons(IEnumerable<EdgeRing> shellList)
		{
			IList<IGeometry> list = new List<IGeometry>();
			foreach (EdgeRing edgeRing in shellList)
			{
				IPolygon item = edgeRing.ToPolygon(this._geometryFactory);
				list.Add(item);
			}
			return list;
		}

		public bool ContainsPoint(Coordinate p)
		{
			using (List<EdgeRing>.Enumerator enumerator = this._shellList.GetEnumerator())
			{
				while (enumerator.MoveNext())
				{
					if (enumerator.Current.ContainsPoint(p))
					{
						return true;
					}
				}
			}
			return false;
		}

		private readonly IGeometryFactory _geometryFactory;

		private readonly List<EdgeRing> _shellList = new List<EdgeRing>();
	}
}
